@L|}6CD l0C)HCC WhL/h `CmCDiD`  R@P1  Y0@R !L` D  C D     )16CS S)  C)D1 p p 0 C9DI pCDL~CiCDiD` D  C D     )16CS S)  C)D1 p p }0 C9DI pCDL~CiCDiD` DD˙` d J)L !}D L(( LL()  L| L( S LH 0p n  } CY?  q  L L  ` )} `A! d߰")-݆ "  $G@LLL&0") $G% }H0 3S8`G ȱG ȱG   Gȭ Gȭ GG}GHiH8(()) G$H% `(0 })8` d)L ݆ & LGȘ ݆LL d  ! LL d)N>Q  HH) }  hyhyB q L> Lm JJ  Ln*` dB%' }8  H H` 1 { LL   !L     Hh SY?  q  1L }  !? S   q 1 L   Ll  Lg E`L   !L)  q 1L}) `L0AM݊L݉ ML  N݆LLLNLMLHG!@}1F GȱGLLEEȩÑEȑEEȑE Ed E7EȩE  q} L !,0,0SGɛ L 1 !L EHEh W G gLLSROTCES EERF } G) *Gȩ GȽG GȌd q q G`  8   0G  `D}CEDC0X:Ȣ Y ȱC* ? 0.. , 0%n ?A[ 0 : L`L  `, 0`Y}`piH n0)բY? 08`0 }  0$L GGȽG L `8L`L}8`  05G)݁,G)ȱGȱGHh0})Hh` B! 8`8iiiLE`}E8FEh( l0`ɃLL L8^~jj8jHi hEEEiEȱEiE` dTE} H8EEȱEEȩEh J E8   . m  i`LI!)E1FR}1LJ舩9GIH`LJJ`HGHh l`} S gL   8 rii `дCDCG W  }C  Lq` X٨`DOS SYS IIIIIIIIIIIIIIIC`0 ߩ0}}} HE |||DDOS DOSDOS SYS }}}}CDOS SYS} 0`BDELV !B }`LVUQ   ]   TU J ]L!T  #      TU  } L ? .  t`GBJ V~DEHI B V0dV!}QDEHI VF9 ,0 ,0 s0hhL  L` H hDHEh"}DEL8HI   0 HI,0 0  9 .G VLO#},0 L4*IJ`llD1:AUTORUN.SYSNEED MEM.SAV TO LOAD THIS FILE.D1:MEM.SAV J y08 B|DEHI$} V0 0`B;DELV䌚 !B y`@ʆ v s? F0Ξ05: [ BDEHI%} VY8 B V  @  /DE `E:D1:DUP.SYSERROR-SAVING USER MEMORY ON DISKTYPE Y TO &}STILL RUN DOS B;DE J V (` 9 V⪍ ઍ  -'}LLu DEHILV 9 .l 9 .l  `` s$B VBH(}I|DE V BLV nB,DE J V* \*` B V BLVDEHI BLVL)}1u H232435; 1 ;  hh@2 e1i1LHҍ 00) 08 109hh@ Ҡ2e*}1i1232435ޥ<<8L} 3E:}DOS ĠǠĠ NOT...COPYRIGHT 1980 ATARIA. DISK DIRECTORY I. FORMAT DISKB. RUN CARTRIDGE J. ,} DISK C. COPY FILE K. BINARY SAVED. DELETE FILE(S) L. BINARY LOADE. RENAME FILE M. RUN AT ADDRESSF. LOCK F-}ILE N. CREATE MEM.SAVG. UNLOCK FILE O. DUPLICATE FILEH. WRITE DOS FILES9!&x#!7&p))'&X*./)L''-؆莟.}R'S  vW DEHHI 1A#! @ ~0ɛ8A0.) ȅ 1 1i/}il ! 1L NO SUCH ITEMSELECT ITEM OR FOR MENU! 0 .{z:*{}.|~ 1 0 00}JB 18L^%|DLl%DIRECTORY--SEARCH SPEC,LIST FILE? # 0 0 n&|D! 1L NOT A DISK FILE1}N !B 1L " 1 !BDED:}:1BJ|DE 1DEBH2}I 1 h0ߢ 0.  0?詛 1 ~0YЛ 1 "L<" "L 3} BL1TYPE "Y" TO DELETE...DELETE FILE SPECCOPY--FROM, TO?OPTION NOT ALLOWED DUPMAN 192 COPYING---D1:DUPM4}AN.TXT# 0|D .L$A#B#C#JB|DE 1BHIDD#E 1D#0: B5} 1L B#C#C#B# B 1N#$0SYS1}:e#D# d# D# .d#ȽD# d# 𩛙d#X# 1,A#6}PdD#ELO- A.BJdD#E 1 1HH 0hh|DL^%1}:e# Lt% e#dD#EL%7} 1 0 . .0% 1L WILD CARDS NOT ALLOWED IN DESTINATION 0 A.|K@C}//3Hu ξL/L DRIVE TO WRITE DOS FILES TO?WRITING NEW DOS FILESTYPE "Y" TO WRITE DOS TO DRIVE 1.?}D1:DOS.SYSERROR - NOT VERSION 2 FORMAT. , &* բ( 1L `[) 0NΞ 0 L1M) 1@} L BAD LOAD FILELOAD FROM WHAT FILE?) 0 0#B 1L WHAT FILE TO LOCK?) 0 0$B 1L WHAT FILE TO UNLOCK?DUA}P DISK-SOURCE,DEST DRIVES?TYPE "Y" IF OK TO USE PROGRAM AREACAUTION: A "Y" INVALIDATES MEM.SAV. h B}  `)  <0 2 2 0  ,   ,,ޢ* 1L ,K* 1 ~0 0C}FINSERT BOTH DISKS, TYPE RETURNERROR - DRIVES INCOMPATIBLE., 1 ~038  , 1L D}, &*  Lz+, 0 , 1 ~0 +,0 ,L+ ,mm  v,"ǭE}0Ξ, 05,Lt+L +,Hh` NOT ENOUGH ROOMINSERT SOURCE DISK,TYPE RETURNF}INSERT DESTINATION DISK,TYPE RETURN`    `L,8,0( rG} v,(`ߢ) 1* 1 ~0Y`hhL S SL1) 8`NAME OF FILE TO MOVE?- 0 0|DLtH}% A., 1 <0 0 .@L# .BJ 1  DEHIB V L1 ,5 1 <0,L. I} JB|,A#Pd#DE 1 HI BDEHHII 1 B 1 , 1 <0,0Lf- B VJ},A#P, 1 <0 0L#L ߢ) 1* 1 ~0Yj383}mm ݭK}}`8}``|* ? ɛ,`|:(|/ 1L `DESTINATION CANT L}BE DOS.SYS0 0H{ $22Δ $28/L /) $2 Π $2 0 ξM}hAΞB,0 J 1 BޝDEHI,HDE 1HIHIDELSAVE-N}GIVE FILE,START,END(,INIT,RUN)O X0 1`BDEPHI V` X0H 1 L O}0 0 1L0`PLEASE TYPE 1 LETTER,0`hhL <0 1L0LA1 ,;ɛ7,"ɛ:ݦ1ݥP}A"D|ݤD|ȩ:|ȩ|ɛ,,(/+.ީ1 1,ɛ`轤{Q}NAME TOO LONG B VL ` L1I H1EӝDL1|mDiE` V0`8d/8 i:"2!22 1R} L ERROR- 128ɛ+,' 20*.. өw2 1``2TOO MANY DIGITSINVALIDS} HEXADECIMAL PARAMETER800 0 8 00`,0'D800H,ɛh`2L1NEED D1 THRU D4DuT} HEXADECIMAL PARAMETER800 0 8 00`,0'D800H,ɛh`2L1NEED D1 THRU D4DuTECHNICAL REFERENCE NOTES FOR THE 1050 DUPLICATIOR BOARD I. Introduction The main reason for this document is to allow V}the advanced user to enhaces any programs that he/she may wish to create for this new powerful drive system. The 1050 DuplicW}ator Drive enhancement board responds to 26 serial bus commands, as well as having the ability to upload user programs to theX} drive. This is not the kind of project that a beginner should undertake, but even so, it still might give you insight inY}to how the drive works, thus making your efforts to use it more effective. It also will give you a place to start to learn hZ}ow to use the 1050 duplicator for maximum utility. HOWEVER, if you undertake to program the 1050 duplicator, you must realiz[}e that you are totally on your own. DTI will not give any support on how to use any of the information contained in this doc\}ument and DTI is not responsible for any inaccuracy that may be contained herein. II. Over veiw The 1050 duplicator PROM ]}contains two basic systems. The main system is the 1050 drive handler. That is, all of the software that goes into making t^}he 1050 drive act like a 1050. The second part of the duplicator PROM is the BCS system (BASIC COPY SOFTWARE) that handles t_}he analysis, reading and writing of copy protected diskettes. Below the two main systems, exist many smaller sub systems. T`}he subsystems consist of SIO communications, command decodeing, sector reading, track buffer management, disk format sensing,a} sector writing, disk formating, etc, to name a few. The two main themes that we will deal with are using the serial I/O cob}mmands and programming the disk drive. III. Serial bus commands 1. Read Sector Command:R Aux1:Low byte of sector numbec}r Aux2:High byte of sector number Returns:128 or 256 Bytes of data depending on density The read sector command can fund}ction in one of many ways depending on how the drive is configured or what density is being read. In single density, the drie}ve will always return 128 bytes of data. The so called density and a half or 1050 double density also will return 128 bytes f}of data. In double density, however, things get a little complicated. In order to make is so that a double density disk wilg}l boot on the ATARI computer, the first 3 sectors need to be 128 bytes long. So, when you read a double density disk, the drh}ive will return 256 bytes unless you read sectors 1,2 or 3, which will return 128 bytes. Also, you can control the way in whi}ich the drive is read. You can read in either the buffered or unbuffered mode. In the drive there is some memory that is sej}t aside to provide an area in which to buffer on complete track of data. In single and double density, this consists of 18 sk}ectors, in density and a half, this is 26 sectors. The idea is that all of the sectors can be read in one revolution of the l}disk. This amounts to about 208 msec. After the data is in the track buffer, it can be read out without having to get it ofm}f the disk. This is what gives the duplicator much more speed that other none enhanced drive, and other non-buffered drive mon}difications. You also have the option to disable the buffered mode of operation (so called forced slow operation). See so}erial bus command 'b' to set buffering mode of drive. If any problem was found in reading any sector, a serial bus error is rp}eturned back with any data that was found and the hardware status byte is set according to what error was found. In general q}these errors will result the dreaded Error- 144. 2. Write Sector with verify Command:W Aux1:Low byte of sector numberr} Aux2:High byte of sector number Returns:Serial bus error on bad write This command also has many modes of operation deps}ending on what the density of the disk is. In single density the drive expects to recieve 128 bytes of data from the computert}. Also in density and a half, the drive expects to recieve 128 bytes of data. Again in doulbe density, things get complicatu}ed. The drive normally expects to recieve 256 bytes of data except for sectors 1,2 or 3 for which it will only expect 128 byv}tes. If the drive cannot find find the record (bad sector) or does not verify the data in the sector (damaged disk), the drw}ive will return an serial bus error resulting in an error 144. 3. Write Sector without verify Command:P Aux1:Low byte ox}f sector number Aux2:High byte of sector number Returns:Serial bus error for record not found This command is the same asy} the 'W' command except that it does not verify the sector. In general, you do not need to verify the sector unless you havez} a very critical operation. By not verifing, you can write data to the disk faster, as in order to verify, the drive has to {}wait for the sector it just wrote to come all the way around. 4. Read drive status Command:S Aux1:Not used Aux2:Not us|}ed Returns:Four bytes of data indicating status The status command returns data indicating the condition of the drive as f}}ollows: Byte 0:Bit 0:Indicates the last command frame had an error. This bit is not used by the duplicator board. Bit 1~}:Checksum, indicates that there was a checksum error in the last command or data fram .This bit is not used by t}he duplicator board. Bit 2:Indicates that the last operation by the drive was in error. Not used by the dupli}cator board Bit 3:Indicates a write protected diskette. 1=Write protect Bit 4:Indicates the drive motor is on. 1=motor} on Bit 5:A one indicates MFM format (double density) Bit 6:Not used Bit 7:Indicates Density and a Half if 1 Byte 1:Bit 0:F}DC Busy should always be a 1 Bit 1:FDC Data Request should always be 1 Bit 2:FDC Lost data should always be 1 Bit 3:FDC CR}C error, a 0 indicates the last sector read had a CRC error Bit 4:FDC Record not found, a 0 indicates last sector not found } Bit 5:FDC record type, a 0 indicates deleted data mark Bit 6:FDC write protect, indicates write protected disk Bit 7:FDC} door is open, 0 indicates door is open Byte 2:Timeout value for doing a format. Byte 3:not used, should be zero 5. Send c}onfiguration to drive Command:O Aux1:not used Aux2:not used Returns:nothing This command sets the drive up for the nex}t format command. Be sure it is issued before using the standard format command ('!') as the drive sets this block up accord}ing to the disk that is installed. The drive expects to see a 12 byte block of data sent by the commputer of the following f}ormat: Byte 0:Number of tracks, should be set to 40 only Byte 1:Step rate, ignored by the duplicator Byte 2:Sectors p}er track high byte (always should be 0) Byte 3:Sectors per track, low byte, can be 18 or 26 Byte 4:Max head number, shou}ld always be 0 Byte 5:Density, Bit 2=1, double density Byte 6:Bytes per sector high byte Byte 7:Bytes per sector low }byte Byte 8:Drive pressent flag Byte 9-11:not used, should be zero 6. Return Drive Configuration Command:N Aux1:n}ot used Aux2:not used Returns:Drive configuration block described above This command examines the drive configuration }that will determine how the disk will be formated on the next standard format command. 7. Standard Disk Format Command:! } Aux1:not used Aux2:not used Returns:Block of data indicating bad sector map First off, this command does not return a bad }sector map. It does send back 128 or 256 bytes depending on the lenth of the sectors on the disk. If a bad sector is found }the drive will return a serial bus error. You should make sure that the drive configuration is set before a format command i}s issued. 8. Density and a half disk format Command:" Aux1:Not used Aux2:Not used Returns:128 byte block Forces d}rive to format in ATARI 1050 double density (density and a half). Does not really return bad sector map. 9. Custom Sector D}isk Format Command:f Aux1:not used Aux2:not used Returns:SIO error if disk is defective The drive expects a 12}8 byte data frame to be uploaded. The first 12 bytes contain drive configuration info that indicate how the disk is to be fo}rmated. The next 18 or 26 byte indicate the sector skewing to be used in formating. In this way the user can use his/her ow}n custom skew when formating this disk. This command is used by Sparta Dos when formating Ultra Sector Skewing. 10. Return S}peed index Command:? Aux1:not used Aux2:not used Returns:One byte indicating speed index This command does several }things. First, it returns one byte that is to be used by a custom high speed serial handler, and second, it will shift the dr}ive into the high speed mode. Pressently, the drive retruns the value of 10. If the next command frame is still sent out at }the standard buad rate, the drive will automatically shift back. 11. Set drive buffer mode. Command:b Aux1:mode Aux2:n}ot used If Aux1 is 0, then the drive will be put into the track buffered mode. Any other value will put the drive into the }unbuffered mode. This is the command that puts the drive into the so called "forced slow mode". 12. Kill density sensing. } Command:d Aux1:mode Aux2:not used This command can be a bit tricky and could cause erratic operation if the user is no}t aware of what is going on. (Note: this command does not work on duplicator prom version 2.12. It will disable the density s}ensing but cannot re-enable it.) The drive normally will check out the floppy disk when it is booted to see what format and w}hat density was used and configure itself to the disk. Sometimes this can be annoying. Certain copy protected disks can con}fuse the software that does this making the drive inactive for up to 6 seconds or more. In some cases you may wish to disabl}e this. When Aux1=0, density sensing will be enabled, when Aux1=1 density sensing will be disabled. 13. Set load address } Command:s Aux1:Lsb of address Aux2:Msb of address Returns:nothing This command can really get you into trouble if }you do not know what you are doing. This command will allow you to set the address used by the Upload command to any address} withing the drive. Yes any address, it does not care. This command is required before the Upload command is initiated. 14}. Upload data to drive Command:u Aux1:not used Aux2:not used This one is even worse. You can crash the Duplicator so }bad with this command that you will have to power it down and reboot. This command requires that you use the Set Address com}mand first or you will get strange results, possibly killing the duplicator. The disk drive expects to see the computer send} a 256 byte block of data to the drive. After the data is recieve, the drive automatically increments the load address by 25}6 so that uploads can be done with out resetting the address. 15. Custom track format Command:c Aux1:low byte of numb}er of bytes on track Aux2:high byte of number of bytes on track Returns:Error if number of bytes too long This command mus}t be used in conjunction with the Set Address command and the Upload command. The address must be set to $2000 before upload}ing the track information. Also the drive will expect a block of data that is one byte long that contains the rotational tim}e for one revolution of the disk. This is very simple to calculate: t=58,594 * 1/rpm Of course, you only want the integer }part of t. For example, for 288 RPM, t will be 203, and for 272 RPM ( the special slow rpm for 20 sectors ) t will be 215. I}f the amount of data is too long to put on a track in the given time, you will get back an error that will show as an OS erro}r- 144. The data fed to the drive can be anything you wish. If you want a blank track you can upload all 0's or $FF's and th}is will clear the track, however it is recomended that you follow the Western Digital rules for generating a formated track. } 16. Run uploaded program Command:r Aux1:Low byte of run address minus one Aux2:High byte of run address minus one }This is another command that will get you into trouble. It is not recomended for the beginner. This command is used to run }a routine that was uploaded by the user. The contents of AUX1 and AUX2 must be the run address minus one. For example, let }us say you just uploaded a program that has a run address of $32C0. In this case AUX1=$BF and AUX2=$32, get the idea! The r}outine that is called should return the value of $00 in memory address $A0 if no error, or return the value $02 in memory add}ress $A0 if some error was encountered. 17. Seek new track Command:g Aux1:track number Aux2:not used Seeks the t}rack whose number is in Aux1. If the number is out of range, the command will return an error. This command is useful for m}oving the head when using the Custom track format command. 18. Write sector with deleted data Command:w Aux1:low byte }of sector number Aux2:high byte of sector number This command behaves the same as write sector without verify, except th}at a deleted data mark is put on the disk. When this sector is read back, the user will get back an error -144 but the data }recieved will be valid. 19. Analyze Track on diskette Command:a Aux1:Track number to analyze Aux2:not used Retu}rns:256 byte block of data This command is quite involved and does a lot. It analyzes the disk and atempts to figure out wha}t is on the disk. Internally, the drive will try up to six times to find out what is no a track. If it cannot, it will not }return a serial bus error. The block of data data that is returned is of the following format: Byte n+0:Track number Byt}e n+1:Side number Byte n+2:Sector number Byte n+3:Sector lenth Byte n+4:CRC1 Byte n+5:CRC2 Byte n+6:FDC status regi}ster Byte n+7:Sector time (in K milli seconds, where K=1.024) This pattern is repeated for each sector is found. Byte 254} of the block of data is the number of sectors found on the track. Byte 255 is status of the operation as follows: Byte 25}5:0=operation complete, data is good :1=Bad track number :2=Blank track :3=Can not figure out what is on trac}k If error code 3 is returned, it is generally best to retry the analisis process. Sometimes if the disk is in poor cond}ition it may take several trys before good data can be found. Error code 1 is returned if you attempt to do something lik}e analyze track number 100 or something. It should be noted that the track analysis only works with a single density (FM) dis}k. Double density disks will most likely return a blank track error. Bytes n+0 to n+5 is the information that is returned by} the read address FDC command. The 2 CRC bytes are of most likely limited utility. The sector time however could be useful.} This is the amount of time that elapses between successful read address commands. In general, you should find that there i}s 10 to 11 msec between full sectors. If you get 5 ms or less, this is most likely a half sector. 20. Upload sector pattern} and read track Command:m Aux1:track number Aux2:not used Returns:nothing This command expects a 128 byte buffer t}hat contains the pattern of sectors on the track of interest. The contents of the 128 byte buffer is a null ($00) terminated} string that contains the sector pattern. For instance, for a typical disk it may be: 1 3 5 7 9 11 13 15 17 2 4 6 8 10 12 1}4 16 17 0 When the drive encounters the 0, it realizes that it has no more data to read off of the disk because there are} no sectors with a sector number of 0 on an atari disk. Also, you should make sure that the first sector number is unique be}fore uploading a pattern. In the above example, there are two sectors with a sector number of 17. If 17 was the first numbe}r in the string, there could be an ambiguity about which sector 17 to read first. After the operation is complete, a buffer i}nside the drive contains all of the data that was read from the track. See get sector data command below. 21. Get sector d}ata Command:t Aux1:not used Aux2:not used This command can only be done after a "m" command has been performed. }It will then be done the same number of times as there were sectors on the disk. The drive returns a buffer of 128 bytes and} then sets the STATUS according to the status register when that sector was read with the "m" command. To get the status iss}ue a status command ("S") and get the second byte returned. THIS COMMAND MUST BE USED THE SAME NUMBER OF TIMES AS SECTORS UPL}OADED WITH THE M COMMAND AFTER THE M COMMAND HAS BEEN EXECUTED OTHER WISE, STRANGE THINGS MAY RESULT! 22. Upload sector dat}a Command:v Aux1:buffer number Aux2:Status of sector This is another dangerous command, and is somewhat complicated.} This command is used to prepare the buffer that is to write out a track of data to the disk. The buffer number is not the }same as the sector number, and this is where is gets a little confusing. For example, let us say a disk has the following sk}ew: 1 3 5 7 9 11 13 15 17 2 4 6 8 10 12 14 16 17 The corresponding buffer number pattern will be: 0 1 2 3 4 5 6 7 8 }9 10 11 12 13 14 15 16 17 what this means is that data for sector 1 is put in buffer number 0, data for sector 3 is put in }buffer number 1, data for sector 5 is put in buffer number 2, ect..., data for sector 2 will go into buffer number 9, and so }on, I think you get the picture. The sector status is the status you got from the "S" command you would have issued after y}ou did a "t" command to get back the sector data. You must only upload to one buffer number once. If you write to buffer} number 15 twice, you will only have the data that was last writen. This command does not put any data onto the disk. 23. }Upload sector pattern and write buffer to disk Command:n Aux1:not used Aux2:not used This command uploads the patte}rn of sectors that appear on the disk and reflects the order of the data as it was uploaded with the "v" command. The string} of data that indicates the sector skew should be null ($00) terminated. Also, the first sector number should be unique as i}n the read data command "m". The lenth of the buffer is 128 bytes that the drive expects. 24. Change drive RPM Command:}j Aux1:mode Aux2:not used If Aux1=0 drive is put to normal rpm (288). If Aux1=1, then drive is put in special slow rpm} (272). Special slow pot must be installed for this command to function. 25. Change drive hold on time Command:h Aux}1:time in tenths of seconds Aux2:not used This command controls the amount of time that the drive stays on after a rea}d, write, or format type command, or anything that requires that the drive spin. The value in Aux1 is interpreted to be in t}enths of a second. So, If Aux1=10 (default value) drive will spin for 1 second. The smallest recomended value is 1. A valu}e of 0 could give erratic results. IV. Programming the duplicator A. Introduction Yes, you can program the duplicatval}ue of 0 could give erratic results. IV. Programming the duplicator A. Introduction Yes, you can program the duplicator. } To aid you in this effort, legal entry points into the prom will be given and a description of the results of making that ca}ll. A series of jump vectors starting at $E000 allow you to write software that should always work with any revision of the }prom. These vectors will never change (or at least I hope so). Every effort should be made by the programmer not to use any} other entry points that he/she should find. These will not be garenteed. 1. Cold start Location:$E000 Entry:non}e Exit:this routine never exits Description: This routine is the cold start entry and will completely initialize the d}rive and thus, destroy any user program. The same action can also be taken by jumping through the vector at $FFFC. 2. Se}nd NAK Location:$E003 Entry:none Exit:none Description: This routine sends a NAK back to the computer over the serial} bus. 3. Send ERR Location:$E006 Entry:none Exit:none Description: This routine sends a Error code back to computer over }the serial bus. 4. Send ACK Location:$E009 Entry:none Exit:none Description: This routine sends an Acknowledge} back to the computer over the seial bus. 5.Send Complete Location:$E00C Entry:none Exit:none Description: This routin}e send a Complete back to the computer over the serial bus. 6. Do a delay Location:$E00F Entry:none Exit:none Th}is routine just burns up 250 micro seconds of time. Useful for serial bus timing. 7. Get a data from the serial bus Loca}tion:$E012 Entry:ACC=number of bytes to read varialbe BPNT points to buffer area Exit:$A0 contains status of oper}ation : =$00 operation ok : =$01 for time out error :$A1 contains checksum byte from data frame This routine can }be used to get either a data frame or command frame from the serial bus. However, it is recomended that you use the recieve }routine. 8. Put a data byte to the serial bus Location:$E015 Entry:ACC contains data byte to send Exit:none Thi}s routine sends one byte of data to the computer. 9. Compute check sum Location:$E018 Entry:Acc contains data to add t}o varialbe CHKSUM Exit:Acc contains data that was added to CHKSUM This routine is used to calculate the checksum of a gro }up of data that will be sent back to the computer. Before this operation is started, however, the programmer must make sure }that the variable CHKSUM is set to zero ($00). 10. Recieve data from computer Location:$E01B Entry:ACC=number of byte }s to recieve :variable BPNT points to buffer space Exit:returns status in location $A0 : =$00 operation ok : = }$01 timeout : =$02 checksum error 11. Send data to computer Location:$E01E Entry:variable BPNT points to data } to send :ACC= number of bytes to send Exit:none This routine sends a buffer of data back to the computer and pu }ts the proper checksum at the end. a lenth of 0 is 256 bytes. 12. Force FDC interrupt Location:$E021 Entry:none } Exit:none This routine will terminate any action that the Floppy disk controller is taking. 13. Recalibrate head po }sition (seek track zero) Location:$E027 Entry:none Exit:none This routine will reposition the head at track ze }ro and set the variable TRACK to zero. 14. Wait for 5 mSec Entry:none Exit:none This routine burns up 5 milliseco }nds of time. Used in timeing track stepping. 15. Seek track Entry:variable NTRK contains track number to seek Exit:carr }y set if seek error This is the routine to use to goto a particular track. Step rate is 10mSec per track. 16. Calculate } new track/sector number Entry:Aux1 and Aux2 is in the command frame buffer Exit:variable NTRK contains new track numb }er :variable NSEC contains new sector number :Aux1 and Aux2 are destroyed This routine is used to calculate phy }sical sector and track addresses from the logical sector number the atari uses. 17. Read a sector from the disk. Entry:AC }C = sector number to read (1-18) or (1-26 in density and 1/2) :variable BPNT points to buffer area to put data allow wRADRIVCHCL (} + ; }@,""(DRIVE NUMBER TO DEPROGRAM %% @)!@@ FAh@IPAiZ)Ap }@)**DRIVE COMMAND**dAqnAt@x$6-P:'AV,$6-&$AV!Ax@ }!Ay0#-@@",67<,.>:,0 6-?:C:,,(DUPLICATOR DEPROGRAMED104,76,89 };**********************************;; Copy Program;;**********************************;SET $E=$4100SET $491=$4100PRO}C h=*()[$00$00$00$07$00$00][$AD$2A$07$8D$E7$02$85$0E$AD$2B$07$8D][$E8$02$85$0F$AD$28$07$85$0A$8D$04$07][$AD$29$07$85$0B$8D}$05$07$18$60]MODULEDEFINE STRING="CHAR ARRAY"CARD StartCARD EndProgSTRING ban1="֠1", } ban2=" by Jim Patchell", ban3=" concept by Bob Gardner" MODULE ; SYS.ACTDEFINE EOL="$9B"DEFINE OpenBuf = "$}0500"DEFINE OpenBufL = "$00"DEFINE OpenBufH = "$05"STRING copy_right(0) = "(c)1983 Action Computer Services"; Primi}tive IO routinesPROC Clos=*(BYTE d)[$FFA2$A686$CA0$AD0]PROC Output=*(BYTE d, STRING s)[$A684$BA0$4D0] PROC In=*(BYTE d,} STRING s)[$A684$5A0$A586$A2$0$A386]PROC XIOstr=*(BYTE d,x,c,a1,a2,STRING s)[$A0A$A0A$98AA$9D$342$A3A5$AF0$9D$34A$A4A5$9D}$34B$A9$0$9DA8$349$A5B1$9D$348$12F0$18$A5A5$169$9D$344$A6A5$69$0$9D$345$4C$E456$60]PROC Opn=*(BYTE d,STRING s,BYTE m,o)[$}A586$A684$3A0$4CXIOstr]PROC Prt=*(BYTE d,STRING s)[$A586$A684$A2$0$A386$9A0$20XIOstr$AD0$BA9$9D$342$9BA9$4C$E456$60]PROC} Error(BYTE err)[$6C$A$0$1113$8301]PROC Break=*()[$BA$8E$4C1$80A0$98$4C Error]; math library routinesPROC LShift=*()[}$84A4$AF0$8586$A$8526$88$FAD0$85A6$60]PROC RShift=*()[$84A4$AF0$8586$8546$6A$88$FAD0$85A6$60]PROC SetSign=*()[$D3A4$1010}]PROC SS1=*()[$8685$8786$38$A9$0$86E5$A8$A9$0$87E5$AA$98$60]PROC SMOps=*()[$D386$E0$0$310$20SS1$8285$8386$85A5$E10$AA$D3}45$D385$84A5$20SS1$8485$8586$A9$0$8785$60]PROC MultB=*()[$1BF0$CA$C786$AA$15F0$C686$A9$0$8A2$A$C606$290$C765$CA$F6D0$18$ }8765$8785$86A5$87A6$60]PROC MultI=*()[$20SMOps$82A6$1BF0$C686$84A6$15F0$CA$C786$8A2$A$8726$C606$690$C765$290$87E6$CA$F0D0!}$8685$82A5$85A6$20MultB$83A5$84A6$20MultB$4CSetSign]PROC SArgs=*()[$A085$A186$A284$18$68$8485$369$A8$68$8585$69$0$48$98$4"}8$1A0$84B1$8285$C8$84B1$8385$C8$84B1$A8$B9$A0$0$8291$88$F810$11A5$FD0$11E6$4C Break$6308$1109$1819$2113$3323$60]SET $4E4=#}LShiftSET $4E6=RShiftSET $4E8=MultISET $4EE=SArgsPROC ChkErr=*(BYTE r,b,eC)[$1610$88C0$8F0$98$80C0$11F0$4C Error$8A$4A$}4A$4A4A$98AA$9D EOF$60]PROC Break1=*(BYTE err)[$1A2$1186$48$20 Break$68$A8$60]PROC Open=*(BYTE d,STRING f,BYTE m,a2)[$4%}8$A186$A284$A8$A9$0$99 EOF$A8$A1B1$8D OpenBuf $A8$C8$9BA9$2D0$A1B1$99 OpenBuf $88$F8D0$68$A2 OpenBufL $A0 OpenBufH $20Opn$4C&} ChkErr]PROC PrintE=*(STRING s)[$A186$AA$A1A4$A5device]PROC PrintDE=*(BYTE d,STRING s)[$20 Prt$4C ChkErr]PROC Close=*(B'}YTE d)[$20 Clos$4C ChkErr]PROC Print=*(STRING s)[$A186$AA$A1A4$A5device]PROC PrintD=*(BYTE d,STRING s)[$20Output$4C ChkEr(}r]PROC InS=*()[$20In$A084$BD$348$3F0$38$1E9$A0$0$A591$A0A4$60]PROC InputS=*(STRING s)[$A286$AA$A2A4$A5device]PROC Input)}SD=*(BYTE d,STRING s)[$48$FFA9$A385$68]PROC InputMD=*(BYTE d,STRING s,BYTE m)[$48$A186$A284$A0$0$A3A5$A191$68$A2A4]PROC In*}putD=*(BYTE d,STRING s)[$20InS$4C ChkErr]CHAR FUNC GetD=*(BYTE d)[$7A2]PROC CCIO=*()[$A486$A0A$A0A$AA$A4A5$9D$342$A9$0$9D+}$348$9D$349$98$20$E456$A085$4C ChkErr]PROC PutE=*()[$A9$9B]PROC Put=*(CHAR c)[$AA$A5device]PROC PutD=*(BYTE d,CHAR c)[$A,}186$A1A4]PROC PutD1=*()[$BA2$4C CCIO]PROC PutDE=*(BYTE dev)[$A0$9B$F7D0]PROC XIO=*(BYTE d,f,c,a1,a2,STRING s)[$20XIOstr-}$4C ChkErr]PROC CToStr=*()[$D485$D586$20$D9AA$20$D8E6$FFA0$A2$0$C8$E8$F3B1$9D$550$F710$8049$9D$550$8E$550$60]PROC Print.}B=*(BYTE n)[$A2$0]PROC PrintC=*(CARD n)[$20 CToStr$A5device]PROC PNum=*()[$50A2$5A0$20 Output$4C ChkErr]PROC PrintBE=*(BY/}TE n)[$A2$0]PROC PrintCE=*(CARD n)[$20PrintC$4CPutE]PROC PrintBD=*(BYTE d, n)[$A0$0]PROC PrintCD=*(BYTE d, CARD n)[$A0850}$8A$A284$A2A6$20 CToStr$A0A5$4CPNum]PROC PrintBDE=*(BYTE n)[$A0$0]PROC PrintCDE=*(BYTE d,CARD n)[$20PrintCD$A0A5$4CPutDE]1}PROC PrintI=*(INT n)[$A286$AA$A2A4$A5device]PROC PrintID=*(BYTE d,INT n)[$C0$0$1610$48$A186$A284$2DA0$20PutD1$38$A9$0$A1E2}5$AA$A9$0$A2E5$A8$68$4CPrintCD]PROC PrintIE=*(INT n)[$20PrintI$4CPutE]PROC PrintIDE=*(BYTE d,INT n)[$20PrintID$A0A5$4CPu3}tDE]PROC StrB=*(BYTE n, STRING s)[$A286$A384$A2$0$A2A4]PROC StrC=*(CARD n, STRING s)[$A284$20 CToStr$C8$B9$550$A291$88$F814}0$60]PROC StrI=*(INT n, STRING s)[$E0$0$ED10$A085$A186$A284$38$A9$0$A0E5$A8$A9$0$A1E5$AA$98$20 CToStr$C8$B9$550$A291$88$F815}0$60];PROC StrI=*(INT n, STRING s)[$E0$0$ED10$A085$A186$A284;$38$A9$0$A0E5$A8$A9$0$A1E5$AA$98$20 CToStr$E8$8A;$A8$B9$54F$A6}291$88$F8D0$8A$A291$C8$2DA9$A291$60]BYTE FUNC InputB=*()CARD FUNC InputC=*()INT FUNC InputI=*()[$A5 device]BYTE FUNC In7}putBD=*(BYTE d)CARD FUNC InputCD=*(BYTE d)INT FUNC InputID=*(BYTE d)[$13A2$8E$550$50A2$5A0$20InputD$50A9$5A2]BYTE FUNC Va8}lB=*(STRING s)CARD FUNC ValC=*(STRING s)INT FUNC ValI=*(STRING s)[$A485$A586$A0$0$A084$A184$A284$A4B1$A385$A3E6$20A9$C8$A9}4D1$5D0$C8$A3C4$F730$A4B1$2DC9$3D0$A285$C8$A3C4$3610$A4B1$30C9$3030$3AC9$2C10$38$30E9$AA$A1A5$48$A0A5$A$A126$A$A126$18$A065:}$A085$68$A165$A185$A006$A126$18$8A$A065$A085$290$A1E6$C8$A3C4$CA30$A2A5$DF0$38$A9$0$A0E5$A085$A9$0$A1E5$A185$60]MODULEDE;}FINE NULL="0"TYPE BLOCK=[CARD size,next]CARD MemLo=$2E7, MemHi=$2E5BLOCK POINTER FreeListCARD FUNC Alloc(CARD nBytes)<} BLOCK POINTER last, current, target last=FreeList ; start at beginning of list current=FreeList.next=} ;search list for a block of sufficient size WHILE (current<>NULL) AND (current.size} current=current.next OD IF current=NULL THEN RETURN(NULL) ;couldn't find a block FI ;first case - block is e?}xactly the right size IF current.size=nBytes THEN last.next=current.next ; just remove this block target=current @} ; from the free list RETURN(target) FI ;second case - block is bigger than requested current.size==-nBytes A}; remove from count target=current+current.size ;allocate from right end target.size=nBytesRETURN(target)PROC Free(BLOB}CK POINTER target CARD nBYTES) BLOCK POINTER last, current target.size=nBytes last=FreeList current=FreeList.nextC} ;search for position to put freed block WHILE (current<>NULL) AND (currentNULL DO phree=p.size p=p.next I} ODRETURN(phree)MODULEBYTE ARRAY t_buff,imagPROC Siov=$E459()[]MODULE STRING cpy_rgt="Copyright (c) 1985 by EMC"SJ}TRING cpy_rgt1="Copyright (c) 1985 by DTI"PROC div=*()[$85$82$86$83$A9$00$85$86$85$87$A5$84][$05$85$D0$01$60$A2$10$26$82$K}26$83$26][$86$26$87$38$A5$86$E5$84$A8$A5$87$E5][$85$90$04$84$86$85$87$CA$D0$E5$26$82][$26$83$A5$82$A6$83$60]SET $4EA=divL}BYTE FUNC hi=*(CARD a);;returns the high order byte of a;[$86$A0] ;stx $A0 high order byte[$60] ;retrun;BYTM}E FUNC lo=*(CARD a);;returns lo byte of a;[$85$A0] ;sta $A0 low order byte[$60] ;return;CARD FUNC b2c=*(BYTE N}a,b);;takes two byte numbers and makes a CARD;a is LSB and b is MSB;[$85$A0$86$A1$60];PROC Zero=*(CARD a)[$85$A0$86$O}A1$A0$00$98][$91$A0$C8$10$FB$60]PROC p_sio_err(BYTE res CARD sec) Print("Error code is:") PrintBE(res) Print("SectorP} number:") PrintCE(sec)RETURNPROC r_tate(BYTE ARRAY a BYTE len)BYTE i,bi=1b=a(0)DO a(i-1)=a(i) i==+1 UNTIL i = Q}lenODa(i-1)=bPrintE("")RETURNPROC map(BYTE ARRAY buff,sec_map BYTE n CARD p)CARD i,jBYTE a,bBYR}TE ARRAY arryIF n>=0 AND n<3 THEN RETURNFIarry=sec_map+pi=0j=2 ;index of sector in buffDO arry(i)=buff(j) i==+1 jS}==+8 UNTIL i=nODDO i=0 a=arry(i) i==+1 DO b=arry(i) IF a=b THEN r_tate(arry,n) EXIT FI i==+1 UNTIL i=nT} OD UNTIL i=nODRETURNBYTE FUNC m_stat(BYTE cmd)BYTE ARRAY rd_cmd="RSat", wd_cmd="Wmuvwnc"BYTE i,len,a U} i=1len=rd_cmd(0)DO a=rd_cmd(i) IF a=cmd THEN RETURN(64) FI i==+1 UNTIL i=len+1ODi=1len=wd_cmd(0)DO a=wd_cmV}d(i) IF a=cmd THEN RETURN(128) FI i==+1 UNTIL i=len+1ODRETURN(0)BYTE FUNC sio(BYTE dev,drv,cmd CARD buf,cnt,sec)BW}YTE ARRAY dcb=$300 CARD ARRAY dcbc=$300BYTE stat=$303dcb(0)=devdcb(1)=drvdcb(2)=cmdstat=m_stat(cmd)dcbc(2)=bufdcbc(4X})=cntdcbc(5)=secSiov()RETURN(stat)BYTE FUNC set_adr(BYTE drv CARD adr)BYTE rCARD nana=0r=sio($31,drv,'s,na,na,adr)Y}RETURN(r)BYTE FUNC seek(BYTE drv,track)BYTE rCARD nar=sio($31,drv,'g,na,na,track)RETURN(r)BYTE FUNC cust(BYTE drv Z}BYTE POINTER t CARD i)BYTE rCARD lenlen=1r=sio($31,drv,'c,t,len,i)RETURN(r)BYTE FUNC up_load(BYTE drv BYTE ARRAY b[}uff)BYTE rr=sio($31,drv,'u,buff,$100,0)RETURN(r)CARD FUNC gen_blk(BYTE d CARD a,i,c) BYTE ARRAY fCARD cntf=acnt\}=0DO f(i)=d i==+1 cnt==+1 UNTIL cnt=cODRETURN(i)CARD FUNC gen_sec(BYTE ARRAY a,skew CARD i BYTE s,t) CARD cntcnt]}=10i=gen_blk(255,a,i,cnt)cnt=4i=gen_blk(0,a,i,cnt)a(i)=$FEi==+1a(i)=t ;track numberi==+1a(i)=0 ;side numberi==+1a(i^})=skew(s);sector numberi==+1a(i)=0 ;sector lenth =128i==+1a(i)=$F7 ;gen CRCi==+1cnt=11i=gen_blk(255,a,i,cnt)cnt=6i=g_}en_blk(0,a,i,cnt)a(i)=$FBi==+1cnt=128i=gen_blk(255,a,i,cnt)a(i)=$F7i==+1RETURN(i)CARD FUNC gen_fmt(BYTE track BYTE `}ARRAY a,skew)CARD iBYTE sec,n,bi=0;;find number of sectors;n=0DO b=skew(n) IF b=0 THEN EXIT FI n==+1ODsec=0a}DO i=gen_sec(a,skew,i,sec,track) sec==+1 UNTIL sec=nODRETURN(i)PROC wait()PrintE("Press any key to continue")GetD(b}7)RETURNPROC insert_disk(BYTE t,src,des);;type=0, source disk;type=1, destination disk;type=2, source and destination c}disk;CARD dumSTRING in_src=""STRING in_des=""STRING in_both="d}"BYTE resIF t=0 THEN res=sio($31,src,'d,dum,dum,1) PrintE(in_src) wait() res=sio($31,src,'d,dum,dum,0e})ELSEIF t=1 THEN res=sio($31,src,'d,dum,dum,1) PrintE(in_des) wait() res=sio($31,src,'d,dum,dum,0)ELSEIF t=2 THEN res=f}sio($31,src,'d,dum,dum,1) res=sio($31,des,'d,dum,dum,1) PrintE(in_both) wait() res=sio($31,src,'d,dum,dum,0) res=sio($31g},des,'d,dum,dum,0)FIRETURNBYTE FUNC yesno(BYTE ARRAY a)DO PrintE(a) PrintE("( or )") a=GetD(7) a=a & $7F IF a='Yq}b'DOS SYSb*+DUP SYSbUTECHNOTEDATb PROGRAM BASbzDUPCODE SCRbFORMAT ACTbUPLOAD ACTbANLTRK ACTbDWNLOAD ACTbDUPSYS ACTDISK DIRb DIR ACT or a='y THEN RETURN(1) ELSEIF a='n OR a='N THEN RETURN(0) ELSE PrintE("Incorrect Reponse") PutE() FIODRETURN(0)r}BYTE FUNC ck_drv(BYTE d)IF d=0 OR d>4 THEN PrintE("ɠ") PutE() RETURN(1)FIRETURN(0)PROCs} h_exit()BYTE a,dCARD dumPrintE("Press to exit") PrintE("Any other key to continue")a=GetD(7)a=a & $7FIF a ='E or t}a='e THEN a=yesno("") Print("Which drive?") d=InputB() IF a THEN sio('1,d,'b,dum,dum,1)u} ELSE sio('1,d,'b,dum,dum,0) FI a=yesno("Reboot system?") IF a THEN [$6C$FC$FF] FIFIRETURNPROC print_ban()Put($v}7D)PrintE(ban1)PrintE(ban2)PrintE(ban3)PrintE(cpy_rgt)PrintE(cpy_rgt1)RETURN PROC main()BYTE ARRAY buff,sec_map,bufw}r,tempBYTE ARRAY stat=$02EA,sec_statBYTE ARRAY n_buff(40)BYTE src_drv,res,n,a,i,j,blk,t_timeBYTE des_drv,rtrys,max_trys,bx}CARD stat_p,m_sec,t_secCARD sec,len,track,k,bfr,data,size,cCARD trk,s_m,phree,dumClose(0)Close(7)Open(0,"E:",12,0)Opey}n(7,"K:",4,0)AllocInit()print_ban()DO Print("Input source drive number:") src_drv=InputB() a=ck_drv(src_drv) UNTIL a=0z}ODDO Print("Input destination drive:") des_drv=InputB() a=ck_drv(des_drv) UNTIL a=0ODbuff=Alloc(256)sec_map=Alloc(32{}0)temp=Alloc(128)sec_stat=Alloc(256)imag=Alloc(3500)phree=PrintFreeList()phree=(phree/128)*128t_buff=Alloc(phree)res=s|}io($31,src_drv,'b,buff,len,1)Print("Number of Retrys (1-255):")max_trys=InputB()m_sec=phree/128IF max_trys<8 THEN max_tr}}ys=8FIDOh_exit()IF des_drv=src_drv THEN insert_disk(0,src_drv,des_drv)ELSE insert_disk(2,src_drv,des_drv)FItrack=0D~}O bfr=t_buff t_sec=0 trk=track DO Print("Track ") PrintC(track) stat_p=t_sec len=256 rtrys=0 DO res=sio($3}1,src_drv,'a,buff,len,track) IF res<>1 THEN p_sio_err(res,sec) FI a=buff(255) IF a<>3 THEN EXIT ELSEI}F rtrys=max_trys THEN b=yesno("䬠") IF b THEN rtrys=0 ELSE EXIT FI }FI rtrys==+1 OD IF a=2 THEN PrintE(" ") n_buff(track)=0 ELSE n=buff(254) IF n=0 THEN P}rintE("") n_buff(track)=0 ELSEIF n > m_sec-t_sec THEN EXIT ELSE n_buff(track)=n t_sec==+n} Print(" ") PrintBE(n) len=128 map(buff,sec_map,n,stat_p) Zero(temp) i=0 DO temp(i)}=sec_map(stat_p+i) i==+1 UNTIL i=n OD res=sio($31,src_drv,'m,temp,len,track) sec=0 DO res=sio('1,src_}drv,'t,bfr,len,sec) res=sio('1,src_drv,'S,stat,4,0) a=stat(1) ;FDC status k=stat_p+sec sec_stat(k)=a a = }a ! $FF IF (a & $08)>0 THEN PrintE("") FI IF (a & $10)>0 THEN PrintE("")} ELSEIF (a & $20)>0 THEN PrintE("") FI sec==+1 bfr==+128 UNTIL sec=n OD FI }FI track==+1 IF track=40 THEN EXIT FI k=m_sec-t_sec UNTIL k<18 OD IF des_drv=src_drv THEN insert_disk(1,src_}drv,des_drv) FI bfr=t_buff j=t_sec t_sec=0 DO Print("Track ") PrintC(trk) stat_p=t_sec i=0 DO buff(i)=0 }i==+1 UNTIL i=128 OD n=n_buff(trk) IF n=0 THEN PrintE(" ") Zero(buff) Zero(buff+128) i}=0 set_adr(des_drv,8192) DO res=up_load(des_drv,buff) i==+1 UNTIL i=12 OD t_time=$D0 size=3000 }seek(des_drv,trk) cust(des_drv,@t_time,size) ELSE IF n=20 THEN PrintE("") sec=1 sio('1,des}_drv,'j,dum,dum,sec) ELSEIF n>20 THEN PrintE("ԠŠ֠Ӡˡ") FI i=0 DO buff(i)=sec_map(}i+stat_p) i==+1 UNTIL i=n OD IF n=20 THEN t_time=215 ELSE t_time=203 FI size=gen_fmt(trk,imag,}buff) Print(" ") data=imag c=0 set_adr(des_drv,8192) DO res=up_load(des_drv,data) data==+2}56 c==+256 UNTIL c>size OD seek(des_drv,trk) res=cust(des_drv,@t_time,size) IF res<>1 THEN p_sio_err(re}s,sec) FI blk=0 len=128 t_sec=t_sec+n DO a=sec_stat(stat_p) sec=b2c(blk,a) res=sio('1,des_drv,'v,bf}r,len,sec) stat_p==+1 blk==+1 bfr==+128 UNTIL blk=n OD PrintE(" ") res=sio('1,des_drv,'n,b}uff,len,trk) FI IF n=20 THEN sec=0;slow drive down sio('1,des_drv,'j,dum,dum,sec) FI trk==+1 UNTIL trk=track} OD IF track>=39 THEN EXIT FI IF src_drv=des_drv THEN insert_disk(0,src_drv,des_drv) FIODprint_ban()ODRETURNMODU}LESET EndProg=*SET Start=main F src_drv=des_drv THEN insert_disk(0,src_drv,des_drv) FIODprint_ban()ODRETURNMODU$ ;**********************************;; PROGRAM TO CHECK OUT UPLOAD OF; CUSTOM FORMATS TO THE DUPLICATOR;;**************}********************;PROC Siov=$E459()[]BYTE FUNC hi=*(CARD a);;returns the high order byte of a;[$86$A0] ;stx $A0} high order byte[$60] ;retrunBYTE FUNC lo=*(CARD a);;returns lo byte of a;[$85$A0] ;sta $A0 low order byte[$}60] ;return;PROC pbyt(BYTE a);;print hex of byte a;BYTE temptemp=a & $F0temp=temp RSH 4 ;get low nibbleIF tem}p <$0A THEN temp=temp+'0ELSE temp=temp+7+'0FIPutD(2,temp) ;print upper nibbletemp=a & $0FIF temp <$0A THEN temp=t}emp+'0ELSE temp=temp+7+'0FIPutD(2,temp)RETURNPROC pdat(BYTE a)pbyt(a)PutD(2,' ) ;print space RETURNPROC padr(}CARD a);;print hex value of a;BYTE bb=hi(a)pbyt(b)b=lo(a)pbyt(b)PutD(2,' ) ;print spaceRETURNPROC pasc(BYTE ARRA}Y a);;print out ascii of hex data;BYTE index,datindex=0DO dat=a(index) IF dat<$20 OR dat > $7A THEN dat='. F}I PutD(2,dat) index==+1 UNTIL index=16ODPutDE(2)RETURNBYTE FUNC sio(BYTE dev,drv,cmd CARD buf,cnt,sec)BYTE ARRAY} dcb=$300 CARD ARRAY dcbc=$300BYTE stat=$303dcb(0)=devdcb(1)=drvdcb(2)=cmdIF cmd=$52 OR cmd=$53 OR cmd='d THEN stat=$}40ELSEIF cmd=$57 OR cmd='u THEN stat=$80ELSE stat=0FIdcbc(2)=bufdcbc(4)=cntdcbc(5)=secSiov()RETURN(stat)BYTE FUNC} set_adr(BYTE drv CARD adr)BYTE rCARD nana=0r=sio($31,drv,'s,na,na,adr)RETURN(r)BYTE FUNC seek(BYTE drv,track)BYT}E rCARD nar=sio($31,drv,'g,na,na,track)RETURN(r)BYTE FUNC cust(BYTE drv CARD i)BYTE rCARD nana=0r=sio($31,drv,'c,n}a,na,i)RETURN(r)BYTE FUNC run(BYTE drv CARD adr)BYTE rCARD nana=0r=sio($31,drv,'r,na,na,adr)RETURN(r)BYTE FUNC} up_load(BYTE drv BYTE ARRAY buff)BYTE rr=sio($31,drv,'u,buff,$100,0)RETURN(r)CARD FUNC hdr(BYTE ARRAY a CARD i)CAR}D cntcnt=0DO a(i)=0 cnt==+1 i==+1 UNTIL cnt=172ODa(i)=$FCi==+1cnt=0DO a(i)=0 cnt==+1 i==+1 UNTIL cnt=22ODRE}TURN(i)CARD FUNC gen_sec(BYTE ARRAY a CARD i BYTE s,t) BYTE ARRAY skew=[1 5 9 13 17 2 6 10 14 18 3 7 11 15 4 8 12 16]CAR}D cntcnt=0DO a(i)=0 cnt==+1 i==+1 UNTIL cnt=6ODa(i)=$FEi==+1a(i)=t ;track numberi==+1a(i)=0 ;side numberi==+1a}(i)=skew(s);sector numberi==+1a(i)=0 ;sector lenth =128i==+1a(i)=$F7 ;gen CRCi==+1cnt=0DO a(i)=0 cnt==+1 i==+1 UNT}IL cnt=17ODa(i)=$FBi==+1cnt=0DO a(i)=$FF cnt==+1 i==+1 UNTIL cnt=128ODa(i)=$F7i==+1cnt=0DO a(i)=0 cnt==+1 i=}=+1 UNTIL cnt=12ODRETURN(i)CARD FUNC gen_fmt(BYTE track BYTE ARRAY a)CARD iBYTE seci=0i=hdr(a,i)sec=0DO i=gen_s}ec(a,i,sec,track) sec==+1 UNTIL sec=18 ;eighteen sectorsODRETURN(i)PROC zero(BYTE ARRAY b)CARD ii=0DO b(i)=0 i==}+1 UNTIL i=5000ODRETURNPROC main()BYTE ARRAY buff(5000)BYTE drive,res,trackCARD data,c,i,cntPrint("drive number fo}rmat:")drive=InputB()Print("formating drive:")PrintBE(drive)track=0zero(buff)DO set_adr(drive,8192) PrintE("Gen forma}t") i = gen_fmt(track,buff) Print("Number of bytes in buff=") PrintCE(i) data=buff c=0 DO Print("Upload block:") }PrintCE(c) res=up_load(drive,data) PrintBE(res) data=data+256 ;next block of data c==+256 UNTIL c > i OD Pri}nt("Seeking track:") PrintCE(track) res = seek(drive,track) Print("Formating track:") PrintCE(track) res = cust(drive,i)} track==+1 UNTIL track=40ODRETURN; PROGRAM TO CHECK OUT UPLOAD OF; CUSTOM FORMATS TO THE DUPLICATOR;;**************' ;**********************************;; PROGRAM TO CHECK OUT UPLOAD OF; PROGRAMS TO THE DUPLICATOR;;********************}**************;PROC Siov=$E459()[]BYTE FUNC hi=*(CARD a);;returns the high order byte of a;[$86$A0] ;stx $A0 high }order byte[$60] ;retrunBYTE FUNC lo=*(CARD a);;returns lo byte of a;[$85$A0] ;sta $A0 low order byte[$60] } ;return;PROC pbyt(BYTE a);;print hex of byte a;BYTE temptemp=a & $F0temp=temp RSH 4 ;get low nibbleIF temp <$0A} THEN temp=temp+'0ELSE temp=temp+7+'0FIPutD(2,temp) ;print upper nibbletemp=a & $0FIF temp <$0A THEN temp=temp+'0}ELSE temp=temp+7+'0FIPutD(2,temp)RETURNPROC pdat(BYTE a)pbyt(a)PutD(2,' ) ;print space RETURNPROC padr(CARD a});;print hex value of a;BYTE bb=hi(a)pbyt(b)b=lo(a)pbyt(b)PutD(2,' ) ;print spaceRETURNPROC pasc(BYTE ARRAY a);};print out ascii of hex data;BYTE index,datindex=0DO dat=a(index) IF dat<$20 OR dat > $7A THEN dat='. FI Pu}tD(2,dat) index==+1 UNTIL index=16ODPutDE(2)RETURNBYTE FUNC sio(BYTE dev,drv,cmd CARD buf,cnt,sec)BYTE ARRAY dcb=$}300 CARD ARRAY dcbc=$300BYTE stat=$303dcb(0)=devdcb(1)=drvdcb(2)=cmdIF cmd=$52 OR cmd=$53 OR cmd='d THEN stat=$40ELS}EIF cmd=$57 OR cmd='u THEN stat=$80ELSE stat=0FIdcbc(2)=bufdcbc(4)=cntdcbc(5)=secSiov()RETURN(stat)BYTE FUNC set_a}dr(BYTE drv CARD adr)BYTE rCARD nana=0r=sio($31,drv,'s,na,na,adr)RETURN(r)BYTE FUNC run(BYTE drv CARD adr)BYTE r}CARD nana=0r=sio($31,drv,'r,na,na,adr)RETURN(r)BYTE FUNC up_load(BYTE drv BYTE ARRAY buff)BYTE rr=sio($31,drv,'u,b}uff,$100,0)RETURN(r)PROC zero(BYTE ARRAY buff)BYTE ii=0DO buff(i)=0 i==+1 UNTIL i=0ODRETURNPROC main()BY}TE ARRAY buff(256)BYTE drive,res,a,bCARD adr,c,d,end,count,i,cntBYTE ARRAY f_name(40)Print("File to UpLoad :")InputS(f_}name)Close(1)Open(1,f_name,4,0)Print("Input drive number to UL:")drive=InputB();;get header from file;a=GetD(1)a=Get}D(1)IF a <> $FF THEN PrintE("Are you sure that is") PrintE("the file you want?") RETURNFIc=GetD(1)d=GetD(1)adr = c} + (d LSH 8)Print("Load Address =")PrintCE(adr)c=GetD(1)d=GetD(1)end = c + (d LSH 8)count = end - adr +1PrintCE(count)}res=set_adr(drive,adr)Print("Result of adr set =")PrintBE(res)cnt=0DO i=0 zero(buff) DO a=GetD(1) buff(i)=a i==}+1 cnt==+1 IF cnt = count THEN EXIT FI UNTIL i=256 ;buffer full OD res=up_load(drive,buff) PrintBE(res) UNTIL }cnt=countODPrintE("Running program!")a=GetD(1)a=GetD(1)a=GetD(1)a=GetD(1)c=GetD(1)d=GetD(1)adr = c + (d LSH 8)-1Pri}ntE("At address:")PrintCE(adr)res=run(drive,adr)i=0DO buff(i)=0 i==+1 UNTIL i=256ODres=sio($31,drive,'d,buff,$100,0})Print("Download status=")PrintBE(res)i=0DO a=buff(i) PrintBE(a) i==+1 UNTIL i=256ODRETURN;;********************f,228##***************************** BASIC PROGRAM TO DEPROGRAM 1050 DUP. USE THIS TO TEST##OUT DIFFER }ENT SERIAL COMMANDSTO PROGRAM 1050 DUP.##*****************************##*****************************LINE };;**********************************;; SECTOR ANALYSIS PROGRAM;;**********************************;BYTE ARRAY t_buff(3}072)PROC Siov=$E459()[]BYTE FUNC hi=*(CARD a);;returns the high order byte of a;[$86$A0] ;stx $A0 high order byte} 90 POKE COMMAND HERELINE 130 AUX 1/AUX 2##*****************************"##EXAMPLE POKE 770,98 WHERE THE,98 } IS CODE FOR b6AUX 1 IS 1 REMOVE BUFFER@AUX 1 IS 0 INSTALL BUFFERJ THIS WILL TOGGLE FAST/SLOWTDUP1050 8K T }RACK BUFFER D:PROGRAM.BAS (} + ; ![$60] ;retrunBYTE FUNC lo=*(CARD a);;returns lo byte of a;[$85$A0] ;sta $A0 low order byte[$60] ;retur}n;PROC pbyt(BYTE a);;print hex of byte a;BYTE temptemp=a & $F0temp=temp RSH 4 ;get low nibbleIF temp <$0A THEN te}mp=temp+'0ELSE temp=temp+7+'0FIPut(temp) ;print upper nibbletemp=a & $0FIF temp <$0A THEN temp=temp+'0ELSE temp}=temp+7+'0FIPut(temp)RETURNPROC pdat(BYTE a)pbyt(a)Put(' ) ;print space RETURNPROC padr(CARD a);;print hex valu}e of a;BYTE bb=hi(a)pbyt(b)b=lo(a)pbyt(b)Put(' ) ;print spaceRETURNPROC pasc(BYTE ARRAY a);;print out ascii of h}ex data;BYTE index,datindex=0DO dat=a(index) & $7F IF dat<$20 OR dat > $7A THEN dat='. FI Put(dat) index==}+1 UNTIL index=8ODRETURNBYTE FUNC sio(BYTE dev,drv,cmd CARD buf,cnt,sec)BYTE ARRAY dcb=$300 CARD ARRAY dcbc=$300BYT}E stat=$303dcb(0)=devdcb(1)=drvdcb(2)=cmdIF cmd=$52 OR cmd=$53 OR cmd='a THEN stat=$40ELSEIF cmd=$57 THEN stat=$80EL}SE stat=0FIdcbc(2)=bufdcbc(4)=cntdcbc(5)=secSiov()RETURN(stat)PROC dump_sec(BYTE d CARD track,sec BYTE ARRAY buff)}CARD sector,lenBYTE res,a,i,jBYTE ARRAY stat=$02EABYTE ARRAY asc(8)sector = (track * 18)+ sec len=128res=sio($31,d,};;READ ONE SECTOR FUNCTION;INCLUDE "D2:DUPSYS.ACT";BYTE ARRAY buffer(256)CARD tmpvecPROC dwn_ld(BYTE ARRAY buff) bp!}nt=buff Send(0) ;send 256 byte bufferRETURN PROC decode(BYTE cmd)BYTE r,lnth,save IF cmd = 'd THEN Delay() Ack() !};send ack Delay() Cmplt();send cmplt dwn_ld(buffer) RETURNELSEIF cmd = 'e THEN Delay() Ack() Nts() Sktk() bpnt=buff!}er save=lenth IF ntrk=0 AND ( nsec > 0 AND nsec < 4 ) THEN lenth = 128 ELSE lenth = 0 FI dra = dra ! $20 ;complement!} density r=Rsec(nsec) dra = dra ! $20 ;complement density lnth=lenth lenth=save IF r = 0 THEN Cmplt() Send(lnth) EL!}SE Err() RETURN FI RETURNFINak() ;no command send NakRETURNPROC main();BYTE i;;initialize the command vector !}to;point to our command decode routine;tmpvec = cmdveccmdvec = decodei=0DO buffer(i)=i i==+1 UNTIL i=0ODRETURN ; w!}e have set up the vector ;and we return to RUNBYTE ARRAY buffer(256)CARD tmpvecPROC dwn_ld(BYTE ARRAY buff) bp 8'R,buff,len,sector)IF res<>1 THEN len=4 sio($31,d,'S,stat,len,sector) a=stat(1);fdc status a = a ! $FF IF (a & $80)>0 T}HEN PrintE("") ELSEIF (a & $10)>0 THEN PrintE("") ELSEIF (a & $20)>0 THEN PrintE("}") FIFIPrint("Track:")PrintCE(track)Print("Sector:")PrintCE(sec)i=0DO j=0 DO a = buff(i) pdat(a) } asc(j)=a j==+1 i==+1 UNTIL j=8 OD pasc(asc) PutE() UNTIL i=128ODRETURNPROC del_entry(BYTE i BYTE ARRAY buff)}BYTE j;removes unwanted entry;j=i+8DO buff(i)=buff(j) i==+1 j==+1 UNTIL j=0ODRETURNBYTE FUNC ck_buff(BYTE trk BY}TE ARRAY buff)BYTE i,a,j,countcount=0;sector counti=0DO a=buff(i) IF a <> trk THEN del_entry(i,buff) FI i==+1 } j=0 DO a = a % buff(i) i==+1 j==+1 UNTIL j=7 OD IF a = 0 THEN EXIT FI count==+1 UNTIL i > 200ODRETURN(co}unt)PROC dis_stat(BYTE n BYTE ARRAY buff)BYTE i,a,j,sectorsa = buff(255) ;check statusIF a = 2 THEN PrintE("}") RETURNFIIF a = 1 THEN PrintE("") RETURNFIPrintE(" }")i=0DO j=0 DO IF j=4 THEN j==+2 i==+2 FI a=buff(i) IF a < 100 THEN Put(' ) FI IF a < 10 THEN Put(' ) F}I PrintB(a) Print(" ") i==+1 j==+1 UNTIL j=8 ODPutE()sectors=n * 8UNTIL i=sectorsODRETURNPROC main()BYTE ARRA}Y buff(256)BYTE drive,res,n,aCARD sec,len,trackPrint("Input drive number:")drive=InputB()res=sio($31,drive,'b,buff,len,}sec)DO Print("Input track number:") track=InputC() len=256 res=sio($31,drive,'a,buff,len,track) IF res<>1 THEN Print(}"Error code is:") PrintBE(res) Print("Sector number:") PrintCE(sec) FI Print("Status of operation=") PrintBE(buff(25}5)) n=ck_buff(track,buff) Print("") PrintBE(n) dis_stat(n,buff) DO Print("Dump any sectors?(y or n)"}) a=GetD(7) PutE() IF a='y OR a='Y THEN Print("Input Sector number (1-18)") sec=InputC() IF sec>18 THEN Pr}intE("") ELSE dump_sec(drive,track,sec,buff) FI ELSEIF a='n OR a='N THEN EXIT E}LSE PrintE("Wrong answer !") FI OD PrintE("Press to EXIT") PrintE("Any other to continue") a=GetD(7) IF a='}e OR a='E THEN EXIT FIODres=sio($31,drive,'b,buff,len,sec)RETURN*******************************;BYTE ARRAY t_buff(3H;**********************************;; ACTION RUNTIME LIBRARY FOR; THE EMC DUPLICATOR BOARD;; COPYRIGHT (C) 1985 BY EMC;%}; THIS PROVIDES A BINDING SO THAT; USERS CAN WRITE THEIR OWN PROGRAMS; FOR THE DUPLICATOR;;*****************************%}******;;GLOBAL VARIABLES;SET $E = 0SET $F = 1SET $491=0SET $492=1SET $B5=$FF00;CARD cmdvec BYTE ARRAY gpnt BY%}TE gcnt,flag,chksumBYTE ARRAY pntrBYTE rlen,slenCARD dummyBYTE ARRAY cmd_frame(5)BYTE trackBYTE ARRAY tadrs(6)BYTE ntr%}k,nsecBYTE ARRAY stradrBYTE tc,mflag,tinb BYTE ARRAY stat(4)BYTE tempBYTE ARRAY bpntBYTE lenth,rtrysCARD err_cnt B%}YTE ARRAY config(12)BYTE drclsd,sect,trckBYTE POINTER baseBYTE mlplr,mcandCARD prodBYTE ARRAY bpnt1,trkch(18)BYTE sign%}CARD atmp;;I/O PORTS R6532;BYTE Dra=$280,Drb=$282,Ddra=$281BYTE Ddrb=$283 ;;FDC 2793;BYTE Streg=$400,Cmdreg=$400BYT%}E Trkreg=$401,Secreg=$402BYTE Datreg=$403;;page zero defs;;;track buffer is at $2000;memory address for programs start%}s;at $3200 and ends at $3fff;;to insure that the action compiler;puts the code at the right address;use the following me%}thode;1. $E and $491 are initially set; to $6000. Compile the code;2. change so the $E and $491 are; set to $6100.;%}3. Now run RELOC.ACT to relocate the; Code to $3200;SET $E=$00SET $F=$32SET $491=$00 ;puts code at $6000SET $492=$32&}SET $B5 = 0SET $B6 = 0;;Runtime Routines;Call these to get at code in prom;;;ACTION SYSTEM ROUTINES;PROC LShift=$E05&}4()PROC RShift=$E057()PROC MultI=$E05A()PROC DivI=$E05D()PROC SArgs=$E060()SET $4E4=LShiftSET $4E6=RShiftSET $4E8&}=MultISET $4EA=DivISET $4EE=SArgsPROC Cold=$E000();Cold start. Call this ;only if you want to &} ;reinit the 1050. Kills ;usr programPROC Nak=$E003();Send NAK back to atariPROC Err=$E006();Send error &}to atariPROC Ack=$E009();Send ACK to atariPROC Cmplt=$E00C();Send completePROC Delay=$E00F();Wait 250uSECPROC Put=$E0&}15(BYTE a) ;put data to atariPROC Fint=$E021() ;force interruptPROC Moon=$E045() ;turn on motorPROC Mooff=$E048() ;turn&} motor offBYTE FUNC Hi=$E066(CARD a)BYTE FUNC Low=$E063(CARD a)PROC Send=$E01E(BYTE l) ;send bufferBYTE FUNC Rsec=$E0&}33(BYTE s) ;read sectorPROC Nts=$E030() ;calculate new trk/secPROC Sktk=$E02D() ;MODULE ;User Program followsBY EMC;$u DOS SYS 039 DUP SYS 042 TECHNOTEDAT 186 PROGRAM BAS 009 DUPCODE SCR 122 FORMAT ACT 028 UPLOAD A* }CT 023 ANLTRK ACT 031 DWNLOAD ACT 008 DUPSYS ACT 019198 FREE SECTORS 198 FREE SECTORS(fPROC DIR() CARD ARRAY ASI(17) INT B CLOSE (1) OPEN (1,"D:*.*",6,0) FOR B=0 TO 20 DO INPUTSD (1,ASI) PRINTF(A. }SI) PRINTF(" ") INPUTSD (1,ASI) PRINTF(ASI) PRINTF("%E") OD CLOSE (1)RETURN,d